/*
 * Project: Set Sample
 * Copyright (C) 2013 alf.labs gmail com,
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.alflabs.setsample.stats;

import android.content.Context;
import com.alflabs.annotations.NonNull;
import com.alflabs.annotations.Null;
import com.alflabs.serial.AsyncSerialFile;
import com.alflabs.utils.RSparseArray;
import com.alflabs.serial.SerialReader;
import com.alflabs.serial.SerialWriter;

public class SetStats {

    private final RSparseArray<SetLevel> mLevels = new RSparseArray<SetLevel>();

    private final AsyncSerialFile mFile = new AsyncSerialFile("set_stats.srl");
    private int mCounter;
    private boolean mChanged;

    public SetStats() {
    }

    @Null
    public SetLevel getLevel(int index) {
        if (index < 0 || index > mLevels.size()) return null;
        return mLevels.valueAt(index);
    }

    @NonNull
    public SetResult addStat(int levelNum, long timeCounterMs) {
        SetLevel setLevel = mLevels.get(levelNum);
        if (setLevel == null) {
            setLevel = new SetLevel(levelNum);
            mLevels.put(levelNum, setLevel);
        }
        mChanged = true;
        return new SetResult(setLevel.getLevel(),
                             setLevel.add(timeCounterMs),
                             timeCounterMs,
                             setLevel.getFastestTimeMs(),
                             setLevel.getAvgTimeMs());
    }

    @NonNull
    SerialWriter toSerial() {
        SerialWriter sw = new SerialWriter();
        int n = mLevels.size();
        sw.addInt("NumLevels", n);
        for (int i = 0; i < n; i++) {
            SetLevel v = mLevels.valueAt(i);
            if (v != null) {
                sw.addSerial("mLevels_" + i, v.toSerial());
            }
        }
        return sw;
    }

    void fromSerial(@NonNull SerialReader sr) {
        mLevels.clear();
        int n = sr.getInt("NumLevels");
        for (int i = 0; i < n; i++) {
            SerialReader s2 = sr.getSerial("mLevels_" + i);
            if (s2 != null) {
                SetLevel tl = new SetLevel(0);
                tl.fromSerial(s2);
                mLevels.put(tl.getLevel(), tl);
            }
        }
    }

    public void beginLoadAsync(Context context) {
        if (mCounter == 0) {
            mFile.beginReadAsync(context);
        }
    }

    public void endLoadAsync() {
        mFile.endReadAsync();
        if (mFile.hasSerial("data")) {
            int newCounter = mFile.getInt("counter", 1);
            if (mCounter < newCounter) {
                mCounter = newCounter;
                SerialReader sr = mFile.getSerial("data");
                assert sr != null;
                //noinspection ConstantConditions
                if (sr != null) fromSerial(sr);
                mChanged = false;
            }
        }
    }

    public void beginSaveAsync(Context context) {
        if (mChanged) {
            mChanged = false;
            mCounter++;
            mFile.putInt("counter", mCounter);
            mFile.putSerial("data", toSerial());
            mFile.beginWriteAsync(context);
        }
    }

    @Override
    public String toString() {
        return "SetStats{" +
                "mLevels=" + mLevels +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        SetStats setStats = (SetStats) o;

        //noinspection RedundantIfStatement
        if (!mLevels.equals(setStats.mLevels)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return mLevels.hashCode();
    }
}
